from abc import ABC, abstractmethod
from datetime import datetime
from types import ModuleType
from typing import List
from pytz import timezone
from dataclasses import dataclass
from importlib import import_module

from vnpy.trader.constant import Interval, Exchange
from vnpy.trader.object import BarData, TickData, FutBasicData, FutMappingData
import pandas as pd
# from .setting import SETTINGS
from pandas import DataFrame

# DB_TZ = timezone(SETTINGS["database.timezone"])
DB_TZ = timezone("Asia/Shanghai")


def convert_tz(dt: datetime) -> datetime:
    """
    Convert timezone of datetime object to DB_TZ.
    """
    dt: datetime = dt.astimezone(DB_TZ)
    return dt.replace(tzinfo=None)


@dataclass
class BarOverview:
    """
    Overview of bar data stored in database.
    """

    symbol: str = ""
    exchange: Exchange = None
    interval: Interval = None
    count: int = 0
    start: datetime = None
    end: datetime = None


class BaseDatabase(ABC):
    """
    Abstract database class for connecting to different database.
    """

    @abstractmethod
    def save_bar_data(self, bars: DataFrame) -> bool:
        """
        Save bar data into database.
        """
        pass

    @abstractmethod
    def save_fut_basic(self, futs: DataFrame) -> bool:
        """
        Save tick data into database.
        """
        pass

    @abstractmethod
    def save_fut_mapping(self, futs: DataFrame) -> bool:
        """
        Save tick data into database.
        """
        pass

    @abstractmethod
    def save_tick_data(self, ticks: DataFrame) -> bool:
        """
        Save tick data into database.
        """
        pass

    def save_account_data(self, account: DataFrame) -> bool:
        """
        Save tick data into database.
        """
        pass

    @abstractmethod
    def load_bar_data(
            self,
            symbol: str,
            exchange: Exchange,
            interval: Interval,
            start: datetime,
            end: datetime
    ) -> DataFrame:
        """
        Load bar data from database.
        """
        pass

    @abstractmethod
    def load_tick_data(
            self,
            symbol: str,
            exchange: Exchange,
            start: datetime,
            end: datetime
    ) -> DataFrame:
        """
        Load tick data from database.
        """
        pass

    # @abstractmethod
    # def load_fut_basic(
    #     self,
    #     symbol: str,
    #     exchange: Exchange,
    #     start: datetime,
    #     end: datetime
    # ) -> List[FutBasicData]:
    #     """
    #     Load tick data from database.
    #     """
    #     pass

    @abstractmethod
    def delete_bar_data(
            self,
            symbol: str,
            exchange: Exchange,
            interval: Interval
    ) -> int:
        """
        Delete all bar data with given symbol + exchange + interval.
        """
        pass

    @abstractmethod
    def delete_tick_data(
            self,
            symbol: str,
            exchange: Exchange
    ) -> int:
        """
        Delete all tick data with given symbol + exchange.
        """
        pass

    # @abstractmethod
    # def delete_fut_basic(
    #     self,
    #     symbol: str,
    #     exchange: Exchange
    # ) -> int:
    #     """
    #     Delete all tick data with given symbol + exchange.
    #     """
    #     pass

    @abstractmethod
    def get_bar_overview(self) -> DataFrame:
        """
        Return data avaible in database.
        """
        pass

    def quary_fut_basic(self, fut_code: str = "SA", fut_type: str = "0") -> DataFrame:
        """
        Query fut basic from datafeed.
        """
        pass

    @abstractmethod
    def get_all_securities(self) -> DataFrame:
        """
        get_all_securities
        """
        pass

    @abstractmethod
    def save_all_securities(self, security) -> bool:
        """
        save_all_securities
        """
        pass

    @abstractmethod
    def get_all_instruments(self, instrument) -> bool:
        """
        para:
        type: stock, futures, 默认所有
        获取所有合约基础信息
        """
        pass

    @abstractmethod
    def get_price(self):
        pass

    @abstractmethod
    def save_price(self, bars: DataFrame) -> bool:
        pass

    @abstractmethod
    def get_ticks(self) -> DataFrame:
        pass

    @abstractmethod
    def save_ticks(self, ticks: DataFrame) -> bool:
        pass


database: BaseDatabase = None


def get_database(database_type: str = "mongodb", database_name: str = "test") -> BaseDatabase:
    """"""
    # Return database object if already inited
    """"""
    global database
    if database:
        return database

    # Read database related global setting
    # database_name: str = SETTINGS["database.name"]
    module_name: str = f"vnpy.feeds.{database_type}"
    print(database_type)

    # Try to import database module
    try:
        module: ModuleType = import_module(module_name)
    except ModuleNotFoundError:
        print(f"找不到数据库驱动{module_name}，使用默认的SQLite数据库")
        module: ModuleType = import_module("vnpy.feeds.sqlite")

    # Create database object from module

    # self.database: str = SETTINGS["database.database"]
    # self.host: str = SETTINGS["database.host"]
    # self.port: int = SETTINGS["database.port"]
    # self.username: str = SETTINGS["database.user"]
    # self.password: str = SETTINGS["database.password"]
    database = module.Database(database=database_name)
    return database
